﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using System.Reflection;
using ArgusLib.Fractals;
using ArgusLib.Fractals.OrbitFractals;

namespace Fraktalysator
{
	partial class MainForm : ArgusLib.Controls.BaseForm
	{
		internal const string HomepageAdress = "https://fraktalysator.codeplex.com/";

		Forms.FractalParameterForm formFractalParameter;
		Forms.RendererParameterForm formRendererParameter;
		Forms.ImageForm formImage;
		Forms.PreviewImageForm formPreviewImage;
		Forms.ProgressForm formProgress;

		Stack<string> history;

		IFractal _Fractal;
		IFractal Fractal
		{
			get { return this._Fractal; }
			set
			{
				if (this._Fractal != null)
				{
					this._Fractal.CalculationFinished -= fractal_CalculationFinished;
					this._Fractal.CalculationProgressChanged -= fractal_CalculationProgressChanged;
					this._Fractal.CalculationAborted -= fractal_CalculationAborted;
					this._Fractal.WarningReceived -= fractal_WarningReceived;
				}

				if (value != null)
				{
					this._Fractal = value;
					this._Fractal.DataSetDumpFile = FileSystem.FileDataSetDump;
					this._Fractal.CalculationFinished += fractal_CalculationFinished;
					this._Fractal.CalculationProgressChanged += fractal_CalculationProgressChanged;
					this._Fractal.CalculationAborted += fractal_CalculationAborted;
					this._Fractal.WarningReceived += fractal_WarningReceived;
				}
			}
		}

		public MainForm()
		{
			InitializeComponent();
			this.MdiChildrenDockDistance = 10;
			this.history = new Stack<string>();
		}

		protected override void OnLoad(EventArgs e)
		{
			base.OnLoad(e);
			this.MdiClient.BackColor = Color.Black;
			this.InitializeIcons();
			this.InitializeChildForms();
			this.InitializeWindowsMenuItems();

			UIMemory uiMemory = UIMemory.Load();
			if (uiMemory != null)
				this.SetUIMemory(uiMemory);

			this.saveFileDialog.Filter = Localization.Text.SaveFileDialog_FilterImagePng + "|*.png|" +
				Localization.Text.SaveFileDialog_FilterDataXml + "|*.xml";
			this.openFileDialog.Filter = this.saveFileDialog.Filter;

			this.InitializeFractal();

			this.miHelpUpdates_CheckedChanged(this, EventArgs.Empty);
		}

		private void InitializeIcons()
		{
			this.tsFileSave.Image = Resources.Icons16.Save;
			this.miFileSave.Image = Resources.Icons16.Save;
			this.tsFileOpen.Image = Resources.Icons16.Load;
			this.miFileOpen.Image = Resources.Icons16.Load;
			this.miEditBack.Image = Resources.Icons16.Back;
			this.tsEditBack.Image = Resources.Icons16.Back;
		}

		private void InitializeChildForms()
		{
			this.formFractalParameter = new Forms.FractalParameterForm();
			this.formRendererParameter = new Forms.RendererParameterForm();
			this.formImage = new Forms.ImageForm();
			this.formPreviewImage = new Forms.PreviewImageForm();
			this.formProgress = new Forms.ProgressForm();

			this.formFractalParameter.MdiParent = this;
			this.formFractalParameter.StartPosition = FormStartPosition.Manual;
			this.formFractalParameter.Location = new Point(0, 0);
			this.formFractalParameter.CalculationRequested += formFractalParameter_CalculationRequested;
			
			this.formRendererParameter.MdiParent = this;
			this.formRendererParameter.Location = new Point(0, this.formFractalParameter.Bottom);
			this.formRendererParameter.StartPosition = FormStartPosition.Manual;
			this.formRendererParameter.RedrawRequested += formRendererParameter_RedrawRequested;
			
			this.formImage.StartPosition = FormStartPosition.Manual;
			this.formImage.MdiParent = this;
			this.formImage.Location = new Point(this.formFractalParameter.Right, 0);
			this.formImage.ImageSize = new Size(450, 450);
			this.formImage.ImageMouseMove += formImage_MouseMove;
			this.formImage.ImageMouseEnter += formImage_MouseEnter;
			this.formImage.ImageMouseLeave += formImage_MouseLeave;
			this.formImage.RectangleSelected += formImage_RectangleSelected;
			this.formImage.SelectionChanged += formImage_SelectionChanged;
			this.formImage.SelectionChangingBegin += formImage_SelectionChangingBegin;
			this.formImage.ResizeBegin += formImage_ResizeBegin;
			this.formImage.ResizeEnd += formImage_ResizeEnd;
			if (ArgusLib.Environment.Runtime == ArgusLib.Runtime.Mono)
			{
				//this.formImage.SizeChanged += formImage_SizeChanged;
				this.formImage.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
			}
			
			this.formPreviewImage.MdiParent = this;
			this.formPreviewImage.StartPosition = FormStartPosition.Manual;
			this.formPreviewImage.Location = new Point(this.formImage.Right, 0);
			this.formPreviewImage.ClientSize = this.formImage.ClientSize;

			this.formFractalParameter.Show();
			this.formRendererParameter.Show();
			this.formImage.Show();
			this.formPreviewImage.Show();

			//this.formProgress.MdiParent = this;
			this.formProgress.StartPosition = FormStartPosition.CenterParent;
			this.formProgress.CancelRequested += formProgress_CancelRequested;
		}

		private void InitializeWindowsMenuItems()
		{
			this.miWindows.DropDownItems.Clear();
			foreach (Form form in this.MdiChildren)
			{
				if (form == this.formImage)
					continue;
				ArgusLib.Controls.ToolStripMenuItem mi = new ArgusLib.Controls.ToolStripMenuItem();
				mi.Text = form.Text;
				mi.CheckOnClick = true;
				mi.Checked = form.Visible;
				form.TextChanged += new EventHandler((sender, e) => { mi.Text = form.Text; });
				form.VisibleChanged += new EventHandler((sender, e) =>
					{
						if (form.Visible != mi.Checked)
							mi.Checked = form.Visible;
					});
				mi.CheckedChanged += new EventHandler((sender, e) =>
					{
						if (form.Visible != mi.Checked)
							form.Visible = mi.Checked;
					});
				this.miWindows.DropDownItems.Add(mi);
			}
		}

		private void InitializeFractal()
		{
			this.formFractalParameter_CalculationRequested(null, EventArgs.Empty);
		}

		private void StartCalculation(bool addToHistory)
		{
			if (addToHistory == true)
			{
				this.history.Push(this.SaveToString());
				if (this.history.Count > 1)
				{
					this.miEditBack.Enabled = true;
					this.tsEditBack.Enabled = true;
				}
			}
			this.Fractal.StartCalculation();
			this.formProgress.ShowDialog();
		}

		protected override void OnFormClosing(FormClosingEventArgs e)
		{
			UIMemory uiMemory = this.GetUIMemory();
			UIMemory.Save(uiMemory);
			base.OnFormClosing(e);
		}

		private UIMemory GetUIMemory()
		{
			UIMemory uiMemory = new UIMemory();

			uiMemory.FormFractalParameter = new UIMemory.Form(this.formFractalParameter);
			uiMemory.FormImage = new UIMemory.Form(this.formImage);
			uiMemory.FormPreviewImage = new UIMemory.Form(this.formPreviewImage);
			uiMemory.FormRendererParameter = new UIMemory.Form(this.formRendererParameter);
			uiMemory.FormMainWindow = new UIMemory.Form(this);

			uiMemory.ToolStripEdit = new UIMemory.ToolStrip(this.tsEdit);
			uiMemory.ToolStripFile = new UIMemory.ToolStrip(this.tsFile);
			uiMemory.ToolStripStatus = new UIMemory.ToolStrip(this.tsStatus);
			uiMemory.ToolStripToolTip = new UIMemory.ToolStrip(this.tsToolTip);

			uiMemory.FilenameOpenFileDialog = this.openFileDialog.FileName;
			uiMemory.FilenameSaveFileDialog = this.saveFileDialog.FileName;

			uiMemory.CheckForUpdates = this.miHelpUpdates.Checked;

			return uiMemory;
		}

		private void SetUIMemory(UIMemory uiMemory)
		{
			uiMemory.FormFractalParameter.Set(this.formFractalParameter);
			uiMemory.FormImage.Set(this.formImage);
			uiMemory.FormPreviewImage.Set(this.formPreviewImage);
			uiMemory.FormRendererParameter.Set(this.formRendererParameter);
			uiMemory.FormMainWindow.Set(this);
			

			uiMemory.ToolStripEdit.Set(this.tsEdit, this);
			uiMemory.ToolStripFile.Set(this.tsFile, this);
			uiMemory.ToolStripStatus.Set(this.tsStatus, this);
			uiMemory.ToolStripToolTip.Set(this.tsToolTip, this);

			this.openFileDialog.FileName = uiMemory.FilenameOpenFileDialog;
			this.saveFileDialog.FileName = uiMemory.FilenameSaveFileDialog;

			this.miHelpUpdates.Checked = uiMemory.CheckForUpdates;
		}

		private IParameter GetFractalParameter()
		{
			if (this.Fractal == null || this.formFractalParameter.FractalType == this.Fractal.GetType())
				return this.formFractalParameter.InputControl.Parameter;
			else
				return this.Fractal.Parameter;
		}

#if DEBUG
		private void CreateVideo()
		{
			//ArgusLib.x264.Parameter x264Para = new ArgusLib.x264.Parameter();
			//x264Para.VideoWidth = 1280;
			//x264Para.VideoHeight = 720;
			//x264Para.RateControlMethod = ArgusLib.x264.RateControlMethods.CQP;
			//x264Para.ConstantQP = 0;
			//x264Para.FpsNumerator = 24000;
			//x264Para.FpsDenumerator = 1001;

			int duration = 10;//7 * 60 + 27; // 7:27
			int frameCount = 10;// (int)(duration * x264Para.FPS) + 1;

			Parameter paraStart = new Parameter(1280, 720);
			paraStart.Iterations = 1000;
			paraStart.ShownArea = new ComplexArea()
			{
				Center = new System.Numerics.Complex(-0.667232419656334, 0.459041562155264),
				Width = 2 * 16 / 3,
				Height = 6
			};

			Parameter paraEnd = new Parameter(1280, 720);
			paraEnd.Iterations = 100000;
			paraEnd.ShownArea = new ComplexArea()
			{
				Center = new System.Numerics.Complex(-0.667232419656334, 0.459041562155264),
				Width = 1.68128919246548E-13,
				Height = 9.4572517076183254E-14
			};

			double b = Math.Log(paraEnd.ShownArea.Width / paraStart.ShownArea.Width) / frameCount;
			Func<int, double> getWidth = new Func<int, double>((frameNo) =>
				{
					return paraStart.ShownArea.Width * Math.Exp(b * frameNo);
				});

			RendererEscapeTimeSmoother renderer = new RendererEscapeTimeSmoother(new RenderParameterEscapeTimeSmoother()
			{
				AutoSetFrequency = true,
				NumberOfPeriods = 4,
				Coloring = RenderParameterEscapeTimeSmoother.Colorings.HSV,
				SetColor = Color.Black
			});

			for (int i = 0; i < frameCount; i++)
			{
				Parameter para = new Parameter(1280, 720);
				para.Iterations = (int)(i * (paraEnd.Iterations - paraStart.Iterations) / (double)frameCount);
				double width = getWidth(i);
				para.ShownArea = new ComplexArea()
				{
					Center = paraStart.ShownArea.Center,
					Width = width,
					Height = width * 9 / 16
				};

				this.Fractal.Parameter = para;
				this.Fractal.Calculate();
				Bitmap frame = renderer.Render(this.Fractal.DataSet);

				// Encode frame
			}
		}
#endif
	}
}
